
// 从 cc._decorator 命名空间中引入 ccclass 和 property 两个装饰器
const {ccclass, property} = cc._decorator;

// 使用装饰器声明 CCClass
@ccclass
export default class PlayerClass extends cc.Component { // ES6 Class 声明语法，继承 cc.Component
    
    // 使用 property 装饰器声明属性，括号里是属性类型，装饰器里的类型声明主要用于编辑器展示
    // @property(cc.Label)
    // 这里是 TypeScript 用来声明变量类型的写法，冒号后面是属性类型，等号后面是默认值
    // label: cc.Label = null;
    // @property
    // text: string = 'hello';

    
    // 主角跳跃高度
    @property
    jumpHeight: number = 0;

    // 主角跳跃持续时间
    @property
    jumpDuration: number = 0;

    // 最大移动速度
    @property
    maxMoveSpeed: number = 0;

    // 加速度
    @property
    accel: number = 0;

    // 加速度方向开关
    @property
    accLeft: boolean = false;

    @property
    accRight: boolean = false;

    // 主角当前水平方向速度
    @property
    xSpeed: number = 0;

    // 跳跃音效资源
    @property({
        type: cc.AudioClip, // 定义一个 AudioClip 资源对象
    })
    jumpAudio: cc.AudioClip = null;

    public setJumpAction(): cc.ActionInterval {
        // 跳跃上升 cc.moveBy() 移动指定的距离  EaseCubicOut 是按三次函数缓动退出的动作。
        let jumpUp = cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionInOut());
        
        // 下落  EaseCubicIn 是按三次函数缓动进入的动作
        let jumpDown = cc.moveBy(this.jumpDuration, cc.v2(0, -this.jumpHeight)).easing(cc.easeCubicActionIn());

        // 添加一个回调函数，用于在动作结束时调用我们定义的其他方法
        let callback = cc.callFunc(this.playJumpSound, this);
        
        // 不断重复  永远地重复一个动作，有限次数内重复一个动作请使用 repeat 动作，由于这个动作不会停止，所以不能被添加到 cc.sequence 或 cc.spawn 中。 cc.sequence(act1, act2) 顺序执行动作，创建的动作将按顺序依次运行。
        return cc.repeatForever(cc.sequence(jumpUp, jumpDown, callback));
    }
    // LIFE-CYCLE CALLBACKS:
    // onLoad() 回调会在节点首次激活时触发，比如所在的场景被载入，或者所在节点被激活的情况下
    public onLoad (): void {
        // 初始化跳跃动作
        let jumpAction = this.setJumpAction();
        // node.runAction() 执行并返回该执行的动作。该节点将会变成动作的目标。
        this.node.runAction(jumpAction);

        // 初始化键盘输入监听
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
    }
    
    // 用 A 和 D 来控制它的跳跃方向
    public onKeyDown(event): void {
        // 当按键按下时设置一个标志
        switch(event.keyCode) {
            case cc.macro.KEY.a:
                this.accLeft = true;
                break;
            case cc.macro.KEY.d:
                this.accRight = true;
                break;
        }

    }
    public onKeyUp(event): void {
        // 释放键时取消设置标志
        switch(event.keyCode) {
            case cc.macro.KEY.a:
                this.accLeft = false;
                break;
            case cc.macro.KEY.d:
                this.accRight = false;
                break;
        }
    }
    // 当组件或者所在节点调用了 destroy()，则会调用 onDestroy 回调，并在当帧结束时统一回收组件
    public onDestroy(): void {
        // 取消键盘输入监听
        cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
        cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
    }
    // 在每一帧渲染前更新物体的行为，状态和方位。这些更新操作通常都放在 update 回调中
    public update(dt: number): void {
        // 根据当前加速度方向每帧更新速度
        if (this.accLeft) {
            this.xSpeed -= this.accel * dt;
        } else if (this.accRight) {
            this.xSpeed += this.accel * dt;
        }
        // 限制主角的速度不能超过最大值  Math.abs(x) 函数返回指定数字 “x“ 的绝对值
        if (Math.abs(this.xSpeed) > this.maxMoveSpeed) {
            // 如果速度达到极限，使用当前方向的最大速度
            this.xSpeed = this.maxMoveSpeed * this.xSpeed / Math.abs(this.xSpeed); 
        }
        // 根据当前速度更新主角的位置
        this.node.x += this.xSpeed * dt;
    }

    public playJumpSound() {
        // 调用声音引擎播放声音, AudioEngine 与 AudioSource 都能播放音频，它们的区别在于 AudioSource 是组件，可以添加到场景中，由编辑器设置。而 AudioEngine 是引擎提供的纯 API，只能在脚本中进行调用
        cc.audioEngine.playEffect(this.jumpAudio, false); // 主要是用于播放音效  audioEngine.playMusic: 主要是用于播放背景音乐
    }
    start () {

    }

    // update (dt) {}
}
/** 
 * 生命周期回调
 * 一个组件从初始化到激活，再到最终销毁的完整生命周期函数调用顺序为：
 * onLoad -> onEnable -> start -> update -> lateUpdate -> onDisable -> onDestroy
 * onLoad 回调会在节点首次激活时触发，比如所在的场景被载入，或者所在节点被激活的情况下
 * 
*/
